iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0
Mobile Development

Android Studio 30天學習系列 第 22

Android Studio 30天學習-DAY22_Android_ROOM的基本練習

  • 分享至 

  • xImage
  •  

Android_Room的基礎設置練習

Room的概述:

根據Android Studio網站對於Room的說法:

Room is a Database Object Mapping library that makes it easy to access database on Android applications.

簡單來說是一個將資料儲存在手機的本地資料庫且可以方便存取的功能,這個功能是SQLite提供的抽象層。

三個主要的功能

  1. Database:
    資料庫類別:用於存取資料的的
  2. Dao:
    資料存取類別:提供應用程式儲存、更新、刪除...等等的對於資料編輯整理的功能。
  3. Entity:
    資料實體:主要為建立存取資料需要的資料表、資料庫。

依賴(dependence)

這些依賴可以到Android Developers網站的Room頁面搜尋到最新版本。

    //ROOM      (遇到了大概是版本的問題,換了新版本就沒問題了)
    def room_version = "2.4.3"
​
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
​
    // optional - RxJava2 support for Room
    implementation "androidx.room:room-rxjava2:$room_version"
​
    // optional - RxJava3 support for Room
    implementation "androidx.room:room-rxjava3:$room_version"
​
    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"
​
    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"
​
    // optional - Paging 3 Integration
    implementation "androidx.room:room-paging:2.5.0-alpha02"
​

Database

Database是抽象類別

import android.content.Context;
​
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
​
/**
 透過Database類別中的entities陣列來引用MyData的實體類別
 資料綁定的Getter-Setter,資料庫版本,是否將資料導出至文件
 **/
@Database(entities = {MyData.class},version = 1,exportSchema = true)
​
public abstract class DataBase extends RoomDatabase {
​
    public static final String DB_NAME = "DB001.db";   //資料庫名稱,可以自訂
    private static volatile DataBase instance;
​
    public static synchronized DataBase getInstance(Context context){
        if(instance == null){
            instance = create(context); //創立新的資料庫
        }
        return instance;
    }
​
    private static DataBase create(final Context context){
        return Room.databaseBuilder(context,DataBase.class,DB_NAME).build();
    }
    public abstract DataUao getDataUao();   //設置對外接口
}

Dao

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;
​
import java.util.List;
​
​
@Dao
public interface DataUao {
​
    //定義資料庫
    String tableName = "MyTable01";
    /**=======================================================================================*/
    /**簡易新增所有資料的方法*/
    @Insert(onConflict = OnConflictStrategy.REPLACE)//預設萬一執行出錯,REPLACE為覆蓋
    void insertData(MyData myData);
​
    /**複雜(?)新增所有資料的方法*/
    @Query("INSERT INTO "+tableName+"(name,Age,Hobby,Email) VALUES(:name,:Age,:Hobby,:Email)")
    void insertData(String name,String Age,String Hobby,String Email);
​
    /**=======================================================================================*/
    /**撈取全部資料*/
    //**
    // 在要監聽的資料上建立Observable。
    // /
    @Query("SELECT * FROM " + tableName)
   List<MyData> displayAll();
​
    /**撈取某個名字的相關資料*/
    @Query("SELECT * FROM " + tableName +" WHERE id = :id")
    List<MyData> findDataByName(String id);
​
    /**=======================================================================================*/
    /**簡易更新資料的方法*/
    @Update
    void updateData(MyData myData);
​
    /**複雜(?)更新資料的方法*/
    @Query("UPDATE "+tableName+" SET name = :name,Age=:Age,Hobby=:Hobby,Email = :Email WHERE id = :id" )
    void updateData(int id,String name,String Age,String Hobby,String Email);
​
    /**=======================================================================================*/
    /**簡單刪除資料的方法*/
    @Delete
    void deleteData(MyData myData);
​
    /**複雜(?)刪除資料的方法*/
    @Query("DELETE  FROM " + tableName + " WHERE id = :id")
    void deleteData(int id);
​
}

Entity

import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
​
//**    ****TableName****
//  Room預設使用類別名稱作為資料表名稱,若想要資料表使用不同名稱
//  可以設定@Entity註釋的tableName屬性。
//  ###資料表名稱是區分大小寫###
//
//  類似於 tableName的功能,Room 使用屬性名稱作為資料庫中的列名稱。如果希望列具有不同的名稱,請將 @ColumnInfo 註釋加到屬性中。
// /
​
@Entity(tableName = "MyTable01")  //這邊要先取好table的名字,稍後的table設置必須與他相同
public class MyData {
    
    //**    @PrimaryKey
    // 每個實體類別必須至少定義一個主鍵,即使只有一個屬性也須加上@PrimaryKey註釋來註釋該屬性
    // 若希望Room可以為實體自動分配ID,則可設定@PrimaryKey的autoGenerate屬性
    // /
    @PrimaryKey(autoGenerate = true)    //設置是否使ID自動累加
    private int id;
    private String name;
    private String Age;
    private String Hobby;
    private String Email;
​
    public MyData(String name, String Age, String Hobby, String Email) {
        this.name = name;
        this.Age = Age;
        this.Hobby = Hobby;
        this.Email = Email;
    }
    //若實體具有不想被保存的屬性,則可使用@Ignore註釋
    @Ignore//如果要使用多形的建構子,必須加入@Ignore
    public MyData(int id, String name, String Age, String Hobby, String Email) {
        this.id = id;
        this.name = name;
        this.Age = Age;
        this.Hobby = Hobby;
        this.Email = Email;
    }
//**
// Getter-Setter 類別
// "在學習物件導向程式語言的時候,每個人一定都有寫到getter與setter的經驗。"
//
// getter-setter是用來作為物件的私有(private)變數或屬性(field)的公用存取介面(public access interface),
// 也就是宣告兩個public method,
// 一個為getter用來取得private varible,
// 另一個則為setter用來設定private varible。
//
// 這樣的設計目的是為了避免private variable被不當使用,
// 像是可以在setter的設定時可加入一些條件判別或處理來避免變數被設成 unacceptable value。
//
// /
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public String getAge() {
        return Age;
    }
​
    public void setAge(String Age) {
        this.Age = Age;
    }
​
    public String getHobby() {
        return Hobby;
    }
​
    public void setHobby(String Hobby) {
        this.Hobby = Hobby;
    }
​
    public String getEmail() {
        return Email;
    }
​
    public void setEmail(String Email) {
        this.Email = Email;
    }
}

Adapter建立對外轉接功能

這邊我會建立一個java物件的Adapter轉接器,在這邊先建立功能在這裡然後再導向到主程式內部。這個功用就是在往後一個程式容量越來越大後若有相同方法就可以再次使用這個物件。

  1. 刪除資料:
    這個功能是因為一開始進畫面時無法及時顯示當前資料庫已存在的資料,所以建立一個手動刷新畫面來顯示當前已存在的內容。
  2. 更新資料:
    /**更新資料*/
    public void refreshView() {
        new Thread(()->{
            List<Entity> data = DataBase.getInstance (activity).getDataUao().displayAll();
            this.entities = data;
            activity.runOnUiThread(() -> {
                notifyDataSetChanged();
            });
        }).start();
    }
    /**刪除資料*/
    public void deleteData(int position){
        new Thread(()->{
            DataBase.getInstance(activity).getDataUao().deleteData(entities.get(position).getId());
            activity.runOnUiThread(()->{
                notifyItemRemoved(position);
                refreshView();
            });
        }).start();
    }
  1. 創建各個接口在Adapter
    private List<Entity> entities;
    private Activity activity;
    private OnItemClickListener onItemClickListener;

//Adapter
    public Adapter(Activity activities){
        this.activity = activities;
    }
//Entity
    public void setData(List<Entity> entities){
        this.entities = entities;
        notifyDataSetChanged ();
    }
//Interface-OnItemClickListener
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
    }

Interface對外接口

interface的用處是可以在不同的物件之間進行呼叫。

public interface OnItemClickListener {
    void onItemClick(Entity entity);
}

Java主程式

  • 設定更改資料的按鈕點擊事件
        //設定更改資料的事件
        btModify.setOnClickListener((view) ->{
            new Thread(() -> {
                if (nowSelectedData ==null) {
                    runOnUiThread (()->{
                        Toast.makeText (MainActivity2.this,"尚未選擇更改資料",Toast.LENGTH_SHORT).show ();
                    });
                    return;
                }
                String name = editName.getText().toString();
                String Age = editAge.getText().toString();
                String Hobby = editHobby.getText().toString();
                String Email = editEmail.getText().toString();
                Entity data = new Entity (nowSelectedData.getId(),name,Age,Hobby,Email);
                DataBase.getInstance(this).getDataUao().updateData(data);
                runOnUiThread(()->{
                    editName.setText("");
                    editAge.setText("");
                    editEmail.setText("");
                    editHobby.setText("");
                    nowSelectedData = null;
                    myAdapter.refreshView();

                    Toast.makeText(this,"已更新資訊!",Toast.LENGTH_LONG).show();
                });
            }).start();
        });
  • 清空填寫資料的按鈕點擊事件
        //清空填寫資料事件按鈕
        btClear.setOnClickListener((view -> {
            String name = editName.getText().toString();
            String Age = editAge.getText().toString();
            String Hobby = editHobby.getText().toString();
            String Email = editEmail.getText().toString();
            if (name.length() == 0 && Age.length () == 0 && Hobby.length () == 0 && Email.length () == 0) {
                //偵測尚未輸入姓名資料時Toast。
                runOnUiThread (()->{
                    Toast.makeText (MainActivity2.this,"尚未填入資料",Toast.LENGTH_SHORT).show ();
                });
                return;
            }
            editName.setText("");
            editAge.setText("");
            editHobby.setText("");
            editEmail.setText("");
            nowSelectedData = null;
            Toast.makeText (MainActivity2.this,"已清空填寫資料",Toast.LENGTH_SHORT).show ();
        }));
  • 新增資料點擊事件
        /**新增資料**/
        btCreate.setOnClickListener((view -> {
            new Thread(() -> {
                String name = editName.getText().toString();
                String Age = editAge.getText().toString();
                String Hobby = editHobby.getText().toString();
                String Email = editEmail.getText().toString();
                if (name.length() == 0 || Age.length () ==0 || Hobby.length () == 0 || Email.length () == 0) {
                    //偵測尚未輸入姓名資料時Toast。
                    runOnUiThread (()->{
                        Toast.makeText (MainActivity2.this,"資料輸入不完整",Toast.LENGTH_SHORT).show ();
                    });
                    return;
                }
                Entity data = new Entity(name,Age,Hobby,Email);
                DataBase.getInstance(this).getDataUao().insertData(data);
                refrashed();
                runOnUiThread(()->{
                    myAdapter.refreshView();
                    //建立完成後TextView清空輸入資料
                    editName.setText("");
                    editAge.setText("");
                    editHobby.setText("");
                    editEmail.setText("");
                });
            }).start();
        }));
  • 刷新資料表點擊事件
        btRefresh.setOnClickListener(View -> {
            new Thread(()->{
                DataBase.getInstance(this).getDataUao().displayAll();
                refrashed();
                runOnUiThread(()->{
                    myAdapter.refreshView();
                });
            }).start();
        });
  • 副程式
    • 初始化RecyclerView
    //初始化RecyclerView的表格副程式
    public void refrashed(){
//        new Thread(()->{          //這邊已經在一個Thread中了,因此若在主程式中再宣告進另一個Thread中會有問題。
        List<Entity> data = DataBase.getInstance(this).getDataUao().displayAll();

        runOnUiThread(()->{
            recyclerView.setAdapter(myAdapter);

            myAdapter.setData(data);
            myAdapter.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(Entity myData) {
                }
            });
            //**
            // 取得在RecyclerView被選取的資料表欄位並顯示在上方的欄位中。
            // /
            myAdapter.setOnItemClickListener((myData) -> {
                nowSelectedData = myData;
                editName.setText(myData.getName());
                editAge.setText(myData.getAge());
                editHobby.setText(myData.getHobby());
                editEmail.setText(myData.getEmail());
            });
        });
//        });

結果畫面顯示

  • 點擊REFRESH按鈕刷新頁面
  • 點擊RecyclerView欄位資料並更改資料
    • 更改前
    • 更改資料
    • 點擊MODIFY按鈕確定更改
    • 確認更改內容是否有更動
  • 新增資料
    • 輸入資料不完全(有任意欄位為空值會觸發)
  • 清空資料
    • 將輸入的資料清除
    • 尚未填入資料判斷
  • Android Studio查詢資料的方法

    在底部的欄位有一個App Inspection,點擊下去後大概要等一下才會出現然後點擊Refresh按鈕大概就可以出現了。

以上是今天做的ROOM練習


上一篇
Android Studio 30天學習-DAY21_ProgressBar基本設置
下一篇
Android Studio 30天學習-DAY23_Retrofit基本設置
系列文
Android Studio 30天學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言